#include "MQTTPub.h"
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <map>
#include "CMQTTClient.h"
#include "ThreadManager.h"
#include <pthread.h>

#define random(x) (rand()%x)

#define ADDRESS "tcp://gosuncn.kinghowe.cn:1883"
#define TOPIC "ngrokserver/+"
#define CLIENTID "ngrokserverpub"
#define QOS         1
#define TIMEOUT     10000L

using namespace std;

void show_local_time()
{
	time_t timer;
	struct tm *tblock;
	timer = time(NULL);
	tblock = localtime(&timer);
	printf("Local time is: %s  ", asctime(tblock));
}

void pubdelivered(void *context, MQTTClient_deliveryToken dt)
{
	printf("Message with token value %d delivery confirmed\n", dt);
}

void pubconnlost(void *context, char *cause)
{
	printf("\nConnection lost\n");
	printf("     cause: %s\n", cause);
}



void CMQTTPub::initMQTT()
{	


	client.conf.address = ADDRESS;
	client.conf.clientID = CLIENTID;
	client.conf.qos = QOS;
	client.conf.topic = TOPIC;
	client.conf.timeout = TIMEOUT;
	client.conf.type = PUBLISH;
	client.conf.keepAliveInterval = 20;
	client.conf.cleanSession = 1;
	client.conf.connlostCB = &pubconnlost;
	client.conf.deliverycompleteCB = &pubdelivered;
	client.conf.msgCB = nullptr;
	client.conf.context = this;

	client.init();
}

void CMQTTPub::start()
{
	//initMQTT();
}

int CMQTTPub::setID(int id)
{
	m_id = id;
	return 0;
}

int CMQTTPub::getID()
{
	return m_id;
}

void CMQTTPub::pub()
{	
	vector<ThreadData> _threadData;
	m_mtx.lock();
	m_threadData.swap(_threadData);

	int size1 = m_threadData.size();
	int size2 = _threadData.size();
	m_mtx.unlock();

	if (_threadData.size() == 0)
		return;

	vector<ThreadData>::iterator itr = _threadData.begin();
	for (; itr != _threadData.end(); ++itr)
	{
		ThreadData data_ptr = *itr;
		ThreadData* data = &data_ptr;
		CMQTTClent *_client = new CMQTTClent;
		_client->conf.topic = data->conf.topic;
		_client->conf.address = data->conf.address;
		srand((int)time(0));
		int random_num = random(100);
		_client->conf.clientID = data->conf.clientID + to_string(random_num);
		_client->conf.qos = data->conf.qos;
		_client->conf.timeout = data->conf.timeout;
		_client->conf.type = data->conf.type;
		_client->conf.keepAliveInterval = 20;
		_client->conf.cleanSession = 1;
		_client->conf.connlostCB = data->conf.connlostCB;
		_client->conf.deliverycompleteCB = data->conf.deliverycompleteCB;
		_client->conf.msgCB = nullptr;
		_client->conf.context = data->conf.context;
		_client->init();

		int ret = _client->publish((char*)data->data.c_str(), (int)strlen(data->data.c_str()), 0);

		if (ret == 0)
		{
			show_local_time();
			_client->mqttDestory();
			printf("Send data to topic:%s\n",data->conf.topic.c_str());
			printf("Payload:%s\n", data->data.c_str());

		}
		else
			sleep(1);

		delete _client;
		_client = NULL;
	}
}


void CMQTTPub::handleMQTTResult(const MQTTResult& json, const Theron::Address from)
{	
	pthread_t tidp;
	ThreadData attr;
	attr.data = json.json;

	std::string client_id = json.client_id;
	string topic = "ngrokserver_result/" + client_id;

	MQTTConf _conf;
	_conf.topic = topic;
	_conf.address = ADDRESS;
	_conf.clientID = CLIENTID;
	_conf.qos = QOS;
	_conf.timeout = TIMEOUT;
	_conf.type = PUBLISH;
	_conf.keepAliveInterval = 20;
	_conf.cleanSession = 1;
	_conf.connlostCB = &pubconnlost;
	_conf.deliverycompleteCB = &pubdelivered;
	_conf.msgCB = nullptr;
	_conf.context = nullptr;

	attr.conf = _conf;

	m_mtx.lock();
	m_threadData.push_back(attr);
	m_mtx.unlock();

	return;
}

//it has several params
void CMQTTPub::setTimer(int millisecond, int timeid)
{
	printf("Timeid:%d\n", timeid);

	int_timerid_map.insert(pair<int, int>(timeid, timeid));   //first we must save the timeid in int_timerid_map
															//thought the ArgumentM use timeid's address
	map<int, int>::iterator it = int_timerid_map.find(timeid);//it must can be found

	ArgumentM arg;  //secondly we must save the two param(CThread-address and timeid's address) in ArgumentM
	arg.insert(pair<int, void*>(MQTTPUB_THREAD_ADDRESS, reinterpret_cast<void*>(this)));
	arg.insert(pair<int, void*>(MQTTPUB_PARAM_ONE, reinterpret_cast<void*>(&(it->second))));

	m_timeargs_map.insert(pair<int, ArgumentM>(timeid, arg)); //third,we should save the ArgumentM in m_timeargs_map
															//thought the ArgumentM is used in onTimer()
	map<int, ArgumentM>::iterator itr = m_timeargs_map.find(timeid);//it must can be found

	Timer* t = new Timer();

	t->start(millisecond, &onTimer, reinterpret_cast<void*>(&(itr->second)));

	m_timer_map.insert(pair<int, Timer*>(timeid, t));
}

void CMQTTPub::cancelTimer(int timeid)
{
	map<int, Timer*>::iterator itr = m_timer_map.find(timeid);
	if (itr != m_timer_map.end())
	{
		itr->second->stop();
		m_timer_map.erase(itr);
	}

	map<int, int>::iterator it = int_timerid_map.find(timeid);
	if (it != int_timerid_map.end())
	{
		int_timerid_map.erase(it);
	}

	map<int, ArgumentM>::iterator itr1 = m_timeargs_map.find(timeid);
	if (itr1 != m_timeargs_map.end())
	{
		m_timeargs_map.erase(itr1);
	}
}

//it has several params
void CMQTTPub::onTimer(void *args)
{
	int* pInt = nullptr;
	CMQTTPub* pThread = nullptr;

	ArgumentM* p_arg = (ArgumentM*)args;// the ArgumentM has two args;
	ArgumentM::iterator itr = p_arg->find(MQTTPUB_PARAM_ONE);
	if (itr == p_arg->end())
	{
		return;
	}
	pInt = (int*)itr->second;

	itr = p_arg->find(MQTTPUB_THREAD_ADDRESS);
	if (itr == p_arg->end()) {
		return;
	}
	pThread = (CMQTTPub*)itr->second;

	int timeid = *pInt;
	switch (timeid) {
	case MQTTPUB_TIME_ID:
		pThread->pub();
		break;
	default:
		break;
	}
}
